{
  lib,
  stdenv,
  fetchFromGitHub,

  rustPlatform,
  rust-cbindgen,

  cmake,
  pkg-config,

  withDocs ? true,
  doxygen,
  python3Packages,

  boost,
  fmt,
  gtest,
  icu,
  spdlog,
  onetbb,
}:

stdenv.mkDerivation (finalAttrs: {
  pname = "libloot";
  version = "0.25.5";
  # Note: don't forget to also update the package versions in the passthru section

  outputs = [
    "out"
    "dev"
  ]
  ++ lib.optionals withDocs [ "doc" ];

  src = fetchFromGitHub {
    owner = "loot";
    repo = "libloot";
    rev = "refs/tags/${finalAttrs.version}";
    hash = "sha256-l8AdqJ0lZH4rBcf4WV3ju+sIHYam6USXCXTqyRPzgeo=";
  };

  patches = [
    # don't try to build the rust FFI dependencies with cargo, since we build them separately
    ./deps.patch
  ];

  postPatch = ''
    # there seem to have been some changes in header files generated by rust-cbindgen, so we use the new names
    substituteInPlace src/api/plugin.{h,cpp} \
        --replace-fail 'Vec_PluginMetadata' 'Vec<::PluginMetadata>'
  '';

  strictDeps = true;

  nativeBuildInputs = [
    cmake
    pkg-config
  ]
  ++ lib.optionals withDocs [
    doxygen
    python3Packages.sphinx
    python3Packages.sphinx-rtd-theme
    python3Packages.breathe
  ];

  buildInputs = [
    boost
    fmt
    gtest
    icu
    spdlog
    onetbb

    finalAttrs.passthru.libloadorder
    finalAttrs.passthru.esplugin
    finalAttrs.passthru.loot-condition-interpreter
  ];

  cmakeFlags = [
    (lib.cmakeFeature "ESPLUGIN_LIBRARIES" "esplugin_ffi")
    (lib.cmakeFeature "LIBLOADORDER_LIBRARIES" "loadorder_ffi")
    (lib.cmakeFeature "LCI_LIBRARIES" "loot_condition_interpreter_ffi")
    (lib.cmakeFeature "FETCHCONTENT_SOURCE_DIR_TESTING-PLUGINS" "../testing-plugins")
    (lib.cmakeFeature "FETCHCONTENT_SOURCE_DIR_YAML-CPP" "${finalAttrs.passthru.yaml-cpp-src}")
    (lib.cmakeBool "LIBLOOT_BUILD_TESTS" finalAttrs.finalPackage.doCheck)
    (lib.cmakeBool "LIBLOOT_INSTALL_DOCS" withDocs)
  ];

  postConfigure = lib.optionalString finalAttrs.finalPackage.doCheck ''
    cp -r --no-preserve=all ${finalAttrs.passthru.testing-plugins} ../testing-plugins
  '';

  postBuild = lib.optionalString withDocs ''
    sphinx-build -b html ../docs docs/html
  '';

  env.GTEST_FILTER =
    let
      disabledTests = [
        # Some locale related tests fail because they need the LOCALE_ARCHIVE env var to be set to "${glibcLocales}/lib/locale/locale-archive"
        # Due to storage size concerns of `glibcLocales`, we skip this
        "CompareFilenames.shouldBeCaseInsensitiveAndLocaleInvariant"
        "NormalizeFilename.shouldCaseFoldStringsAndBeLocaleInvariant"
      ];
    in
    "-${builtins.concatStringsSep ":" disabledTests}";

  doCheck = stdenv.buildPlatform.canExecute stdenv.hostPlatform;

  passthru = {
    testing-plugins = fetchFromGitHub {
      owner = "Ortham";
      repo = "testing-plugins";
      tag = "1.6.2";
      hash = "sha256-3Aa98EwqpuGA3YlsRF8luWzXVEFO/rs6JXisXdLyIK4=";
    };

    yaml-cpp-src = fetchFromGitHub {
      owner = "loot";
      repo = "yaml-cpp";
      tag = "0.8.0+merge-key-support.2";
      hash = "sha256-whYorebrLiDeO75LC2SMUX/8OD528BR0+DEgnJxxpoQ=";
    };

    buildRustFFIPackage =
      args:
      rustPlatform.buildRustPackage (
        args
        // {
          postConfigure = ''
            cp -r --no-preserve=all ${finalAttrs.passthru.testing-plugins} testing-plugins
          '';

          nativeBuildInputs = [ rust-cbindgen ];

          buildAndTestSubdir = "ffi";

          postBuild = ''
            cbindgen ffi/ -l "$lang" -o "$out/include/$header"
          '';
        }
      );

    libloadorder = finalAttrs.passthru.buildRustFFIPackage rec {
      pname = "libloadorder";
      version = "18.3.0";

      src = fetchFromGitHub {
        owner = "Ortham";
        repo = "libloadorder";
        tag = version;
        hash = "sha256-/8WOEt9dxKFTTZbhf5nt81jo/yHuALPxh/IwAOehi9w=";
      };

      cargoHash = "sha256-re/cKqf/CAD7feNIEuou4ZP8BNkArd5CvREx1610jig=";

      lang = "c++";
      header = "libloadorder.hpp";
    };

    esplugin = finalAttrs.passthru.buildRustFFIPackage rec {
      pname = "esplugin";
      version = "6.1.1";

      src = fetchFromGitHub {
        owner = "Ortham";
        repo = "esplugin";
        tag = version;
        hash = "sha256-ygjSyixg+9HFFNV/G+w+TxGFTrjlWxlDt8phpCE8xyQ=";
      };

      cargoHash = "sha256-6sY2M7kjSYB3+6+zoMxPwdl+g7ARLHm9RdSODHQR8bE=";

      lang = "c++";
      header = "esplugin.hpp";
    };

    loot-condition-interpreter = finalAttrs.passthru.buildRustFFIPackage rec {
      pname = "loot-condition-interpreter";
      version = "5.3.0";

      src = fetchFromGitHub {
        owner = "loot";
        repo = "loot-condition-interpreter";
        tag = version;
        hash = "sha256-MvadQ4cWpzNgF/lUW5Jb758DvfRPGZ7s1W4MbO7nbIw=";
      };

      cargoHash = "sha256-m/vRnAJyMQOosxnjSUgHIY1RCkdB5+HFVqqzYVEpgOI=";

      lang = "c";
      header = "loot_condition_interpreter.h";
    };
  };

  meta = {
    description = "C++ library for accessing LOOT's metadata and sorting functionality";
    homepage = "https://github.com/loot/libloot";
    license = lib.licenses.gpl3Only;
    maintainers = with lib.maintainers; [ tomasajt ];
    platforms = lib.platforms.linux;
  };
})
